/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.fi.muni.diplomka.common.enumeration.MovementType;
import cz.fi.muni.diplomka.common.enumeration.ResourceType;
import cz.muni.fi.diplomka.datalayer.dao.IncomingResourcesDao;
import cz.muni.fi.diplomka.datalayer.entity.IncomingResourcesEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class IncomingResourcesDaoImpl implements IncomingResourcesDao {

    @Override
    public void addIncomingResources(IncomingResourcesEntity incomingResourcesEntity) {
        if (incomingResourcesEntity == null) {
            throw new IllegalArgumentException("Incoming Resources Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            String query = "MATCH (IncomingResources:IncomingResources) RETURN max(IncomingResources.id)";

            Long key = new Long(0);
            ExecutionResult result = engine.execute(query);
            Iterator<Long> number = result.columnAs("max(IncomingResources.id)");
            while (number.hasNext()) {
                key = number.next();
            }

            if (key == null) {
                key = new Long(0);
            }
            incomingResourcesEntity.setId(new Long(key + 1));

            params.put("id", incomingResourcesEntity.getId());
            params.put("cityFrom", incomingResourcesEntity.getCityFrom());
            params.put("cityTo", incomingResourcesEntity.getCityTo());
            params.put("movement", incomingResourcesEntity.getMovementType().toString());
            params.put("resource", incomingResourcesEntity.getResource().toString());
            params.put("resourceCount", incomingResourcesEntity.getResourceCount());
            params.put("timeOfArrival", incomingResourcesEntity.getTimeOfArrival().getTime());

            query = "MATCH (cityFrom:City {id : {cityFrom}}), (cityTo:City {id : {cityTo}}) "
                    + "CREATE (cityFrom)-[r:SENTFROM]->(IncomingResources: IncomingResources {id : {id}, "
                    + "movement : {movement}, resource : {resource}, resourceCount : {resourceCount},"
                    + "timeOfArrival : {timeOfArrival}}) "
                    + "CREATE (IncomingResources)<-[r2:SENTTO]-(cityTo)";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editIncomingResources(IncomingResourcesEntity incomingResourcesEntity) {
        if (incomingResourcesEntity == null) {
            throw new IllegalArgumentException("Incoming Resources Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("id", incomingResourcesEntity.getId());
            params.put("cityFrom", incomingResourcesEntity.getCityFrom());
            params.put("cityTo", incomingResourcesEntity.getCityTo());
            params.put("movement", incomingResourcesEntity.getMovementType().toString());
            params.put("resource", incomingResourcesEntity.getResource().toString());
            params.put("resourceCount", incomingResourcesEntity.getResourceCount());
            params.put("timeOfArrival", incomingResourcesEntity.getTimeOfArrival().getTime());

            String query = "MATCH (IncomingResources:IncomingResources { id :{id}})"
                    + "SET IncomingResources.movement = {movement}, IncomingResources.resource = {resource},"
                    + "IncomingResources.resourceCount = {resourceCount}, IncomingResources.timeOfArrival = {timeOfArrival}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void deleteIncomingResources(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("id", id);

            String query = "MATCH (IncomingResources:IncomingResources{ id : {id}})-[r]-(n) DELETE IncomingResources,r";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public List<IncomingResourcesEntity> getAllIncomingResourcesOfCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        List<IncomingResourcesEntity> incomingResourcesList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);

            String query = "MATCH (CityTo:City{ id : {cityId}})-[r:SENTTO]->(IncomingResources:IncomingResources)<-[r2:SENTFROM]-(CityFrom:City) "
                    + "RETURN IncomingResources, CityTo.id, CityFrom.id";
            ExecutionResult result = engine.execute(query, params);
            ResourceIterator<Map<String, Object>> incomingResources = result.iterator();

            while (incomingResources.hasNext()) {
                Map<String, Object> row = incomingResources.next();
                Node node = (Node) row.get("IncomingResources");

                IncomingResourcesEntity incomingResourcesEntity = new IncomingResourcesEntity();
                incomingResourcesEntity.setId(Long.valueOf(node.getProperty("id").toString()));
                incomingResourcesEntity.setCityFrom(new Long(row.get("CityFrom.id").toString()));
                incomingResourcesEntity.setCityTo(new Long(row.get("CityTo.id").toString()));
                incomingResourcesEntity.setMovementType(MovementType.valueOf(node.getProperty("movement").toString()));
                incomingResourcesEntity.setResource(ResourceType.valueOf(node.getProperty("resource").toString()));
                incomingResourcesEntity.setResourceCount(Integer.valueOf(node.getProperty("resourceCount").toString()));
                incomingResourcesEntity.setTimeOfArrival(new Timestamp(Long.valueOf(node.getProperty("timeOfArrival").toString())));

                incomingResourcesList.add(incomingResourcesEntity);
            }
            t.success();
        }
        return incomingResourcesList;
    }

    @Override
    public List<IncomingResourcesEntity> getAllOutcomingResourcesOfCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        List<IncomingResourcesEntity> outcomingResourcesList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);

            String query = "MATCH (CityTo:City)-[r:SENTTO]->(IncomingResources:IncomingResources)<-[r2:SENTFROM]-(CityFrom:City { id : {cityId}}) "
                    + "RETURN IncomingResources, CityTo.id, CityFrom.id";
            ExecutionResult result = engine.execute(query, params);
            ResourceIterator<Map<String, Object>> incomingResources = result.iterator();

            while (incomingResources.hasNext()) {
                Map<String, Object> row = incomingResources.next();
                Node node = (Node) row.get("IncomingResources");

                IncomingResourcesEntity incomingResourcesEntity = new IncomingResourcesEntity();
                incomingResourcesEntity.setId(Long.valueOf(node.getProperty("id").toString()));
                incomingResourcesEntity.setCityFrom(new Long(row.get("CityFrom.id").toString()));
                incomingResourcesEntity.setCityTo(new Long(row.get("CityTo.id").toString()));
                incomingResourcesEntity.setMovementType(MovementType.valueOf(node.getProperty("movement").toString()));
                incomingResourcesEntity.setResource(ResourceType.valueOf(node.getProperty("resource").toString()));
                incomingResourcesEntity.setResourceCount(Integer.valueOf(node.getProperty("resourceCount").toString()));
                incomingResourcesEntity.setTimeOfArrival(new Timestamp(Long.valueOf(node.getProperty("timeOfArrival").toString())));

                outcomingResourcesList.add(incomingResourcesEntity);
            }
            t.success();
        }
        return outcomingResourcesList;
    }

    @Override
    public List<IncomingResourcesEntity> getAllIncomingResources() {
        List<IncomingResourcesEntity> incomingResourcesList = new ArrayList<>();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            String query = "MATCH (CityTo:City)-[r:SENTTO]->(IncomingResources:IncomingResources)<-[r2:SENTFROM]-(CityFrom:City) "
                    + "RETURN IncomingResources, CityTo.id, CityFrom.id";
            ExecutionResult result = engine.execute(query);
            ResourceIterator<Map<String, Object>> incomingResources = result.iterator();

            while (incomingResources.hasNext()) {
                Map<String, Object> row = incomingResources.next();
                Node node = (Node) row.get("IncomingResources");

                IncomingResourcesEntity incomingResourcesEntity = new IncomingResourcesEntity();
                incomingResourcesEntity.setId(Long.valueOf(node.getProperty("id").toString()));
                incomingResourcesEntity.setCityFrom(new Long(row.get("CityFrom.id").toString()));
                incomingResourcesEntity.setCityTo(new Long(row.get("CityTo.id").toString()));
                incomingResourcesEntity.setMovementType(MovementType.valueOf(node.getProperty("movement").toString()));
                incomingResourcesEntity.setResource(ResourceType.valueOf(node.getProperty("resource").toString()));
                incomingResourcesEntity.setResourceCount(Integer.valueOf(node.getProperty("resourceCount").toString()));
                incomingResourcesEntity.setTimeOfArrival(new Timestamp(Long.valueOf(node.getProperty("timeOfArrival").toString())));

                incomingResourcesList.add(incomingResourcesEntity);
            }
            t.success();
        }
        return incomingResourcesList;
    }
}
